using System.Diagnostics.Contracts;
using OpenLS.Spreadsheet.Expressions;

namespace OpenLS.Spreadsheet
{
    partial class Cell
    {
        private abstract class CellData
        {
            class ValueCellData : CellData, ISetValue
            {
                private object _value;

                protected ValueCellData(object value)
                {
                    this._value = value;
                }

                protected override Expression Expression
                {
                    get { return null; }
                }

                protected override ExtraCellData ExtraData
                {
                    get { return null; }
                }

                protected override CellFormattingRecord FormattingRecord
                {
                    get { return null; }
                }

                protected override object Value
                {
                    get { return _value; }
                }

                public void SetValue(object value)
                {
                    _value = value;
                }
            }
            class ValueFormattingCellData : ValueCellData, ISetFormattingRecord
            {
                private CellFormattingRecord _formattingRecord;

                public ValueFormattingCellData(object value, CellFormattingRecord record) : base(value)
                {
                    _formattingRecord = record;
                }
                protected override CellFormattingRecord FormattingRecord
                {
                    get { return this._formattingRecord; }
                }

                public void SetFormattinRecord(CellFormattingRecord value)
                {
                    _formattingRecord = value;
                }
            }
            class ValueFormattingExpressionCellData : ValueFormattingCellData, ISetExpression
            {
                private Expression _expression;

                public ValueFormattingExpressionCellData(object value, CellFormattingRecord record, Expression expression) : base(value, record)
                {
                    this._expression = expression;
                }
                protected override Expression Expression
                {
                    get { return this._expression; }
                }

                public void SetExpression(Expression value)
                {
                    _expression = value;
                }
            }
            class ValueFormattingExpressionExtraDataCellData : ValueFormattingExpressionCellData, ISetExtraData
            {
                private ExtraCellData _extraData;

                public ValueFormattingExpressionExtraDataCellData(object value, CellFormattingRecord formattingRecord, Expression expression, ExtraCellData extraCellData) : base(value, formattingRecord, expression)
                {
                    this._extraData = extraCellData;
                }
                protected override ExtraCellData ExtraData
                {
                    get { return this._extraData; }
                }

                public void SetExtraData(ExtraCellData value)
                {
                    throw new System.NotImplementedException();
                }
            }

            public static Expression GetExpression(object target)
            {
                CellData cellData = target as CellData;
                if (cellData == null)
                    return null;

                if (cellData == null)
                    return null;
                return cellData.Expression;
            }

            protected abstract Expression Expression { get; }

            public static object SetExpression(object target, Expression expression)
            {
                Contract.Ensures(expression == null || Contract.Result<object>() is CellData && object.Equals(((CellData)Contract.Result<object>()).Expression, expression));
                CellData cellData = target as CellData;
                if (cellData == null)
                {
                    return CellData.Create(GetValue(target), null, expression, null);
                }
                ISetExpression setValue = cellData as ISetExpression;
                if (setValue == null)
                    return CellData.Create(cellData.Value, cellData.FormattingRecord, expression, cellData.ExtraData);
                setValue.SetExpression(expression);
                return cellData;
            }

            protected abstract ExtraCellData ExtraData { get; }

            public static CellFormattingRecord GetFormattingRecord(object target)
            {
                CellData cellData = target as CellData;
                if (cellData == null)
                    return null;
                return cellData.FormattingRecord;
            }

            protected abstract CellFormattingRecord FormattingRecord { get; }

            public static object GetValue(object target)
            {
                CellData cellData = target as CellData;
                if (cellData == null)
                    return target;
                return cellData.Value;
            }

            public static object SetValue(object target, object value)
            {
                Contract.Ensures(value == null || (Contract.Result<object>() != null && object.Equals(GetValue(Contract.Result<object>()), value)));
                CellData cellData = target as CellData;
                if (cellData == null)
                    return value;
                ISetValue setValue = cellData as ISetValue;
                if (setValue == null)
                    return CellData.Create(value, cellData.FormattingRecord, cellData.Expression, cellData.ExtraData);
                setValue.SetValue(value);
                return cellData;
            }

            protected abstract object Value { get;  }

            internal static object Create(object value, CellFormattingRecord formattingRecord, Expression expression, ExtraCellData extraCellData)
            {
                Contract.Assert(!(value is CellData));
                if (extraCellData == null)
                {
                    if (expression == null)
                    {
                        if (formattingRecord == null)
                        {
                            return value;
                        }
                        return new ValueFormattingCellData(value, formattingRecord);
                    }
                    return new ValueFormattingExpressionCellData(value, formattingRecord, expression);
                }
                return new ValueFormattingExpressionExtraDataCellData(value, formattingRecord, expression, extraCellData);
            }

            public static object SetFormattingRecord(object target, CellFormattingRecord formattingRecord)
            {
                CellData cellData = target as CellData;

                if (cellData == null)
                {
                    return Create(GetValue(target), formattingRecord, null, null);

                }
                ISetFormattingRecord i = cellData as ISetFormattingRecord;
                if (i == null)
                {
                    return Create(cellData.Value, formattingRecord, cellData.Expression, cellData.ExtraData);
                }
                i.SetFormattinRecord(formattingRecord);
                return cellData;
            }
            internal interface ISetValue
            {
                void SetValue(object value);
            }
            internal interface ISetFormattingRecord
            {
                void SetFormattinRecord(CellFormattingRecord value);
            }
            internal interface ISetExpression
            {
                void SetExpression(Expression value);
            }
            internal interface ISetExtraData
            {
                void SetExtraData(ExtraCellData value);
            }

            public static CellData Create(bool value, CellFormattingRecord formattingRecord)
            {
                throw new System.NotImplementedException();
            }
        }
    }

}